package cn.suimg.codegenerator;

import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.PsiModifierListImpl;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;

import java.util.Arrays;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 生成属性的get set方法，并将属性上的注释添加到方法上
 * Created by huqiang on 15/8/15.
 * Update by Suimg on 21/7/12
 */
public class GenerateGSAction extends AnAction {

    /**
     * 生成方法类型
     */
    private enum MethodType{
        GET,SET
    }

    /**
     * Getter javadoc 生成模板
     */
    private static final String GETTER_DOC_TEMPLATE =   "/**\n" +
                                                        " * 获取%s\n" +
                                                        " * @return %s\n" +
                                                        " */\n";
    /**
     * Setter javadoc 生成模板
     */
    private static final String SETTER_DOC_TEMPLATE =   "/**\n" +
                                                        " * 设置%s\n" +
                                                        " * @param %s %s\n" +
                                                        " */\n";

    /**
     * Getter javaCode get生成模板
     */
    public static final String GETTER_CODE_TEMPLATE_DEFAULT =   "public %s get%s() { \n" +
                                                                "    return this.%s;\n" +
                                                                "}\n";

    /**
     * Getter javaCode is生成模板
     */
    public static final String GETTER_CODE_TEMPLATE_BOOLEAN =   "public %s is%s() { \n" +
                                                                "    return this.%s;\n" +
                                                                "}\n";

    /**
     * Setter javaCode 生成模板
     */
    public static final String SETTER_CODE_TEMPLATE =   "public void set%s(%s %s) { \n" +
                                                        "    this.%s = %s;\n" +
                                                        "}\n";

    /**
     * 属性名首字母大写
     */
    private final Function<String,String> filedNameToUpperCase = methodName -> methodName.substring(0, 1).toUpperCase() + methodName.substring(1);

    /**
     * 获取PSIMethod
     */
    private final Function<AnActionEvent,PsiClass> getPsiMethodFromContext = event -> {
        PsiFile psiFile = event.getData(LangDataKeys.PSI_FILE);
        Editor editor = event.getData(PlatformDataKeys.EDITOR);
        if (psiFile == null || editor == null) {
            event.getPresentation().setEnabled(false);
            return null;
        }
        //用来获取当前光标处的PsiElement
        int offset = editor.getCaretModel().getOffset();
        PsiElement esiElement = psiFile.findElementAt(offset);
        return PsiTreeUtil.getParentOfType(esiElement, PsiClass.class);
    };

    @Override
    public void actionPerformed(@NotNull AnActionEvent event) {
        generateGSMethod(getPsiMethodFromContext.apply(event));
    }

    /**
     * 插件加载方法
     * @param psiMethod
     */
    private void generateGSMethod(final PsiClass psiMethod) {
        WriteCommandAction.writeCommandAction(psiMethod.getProject(), psiMethod.getContainingFile()).run(() -> createGetSet(psiMethod));
    }

    /**
     * 插件运行主方法
     * @param psiClass
     */
    private void createGetSet(PsiClass psiClass) {
        PsiField[] fields = psiClass.getFields();
        Set<String> methodSet = Arrays.stream(psiClass.getMethods()).map(PsiMethod::getName).collect(Collectors.toSet());
        PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(psiClass.getProject());

        for (PsiField field : fields) {

            //如果有final修饰符就不生成GET/SET方法
            PsiModifierList modifierList;
            if((modifierList = field.getModifierList()) != null){
                if (modifierList.hasModifierProperty(PsiModifier.FINAL)) {
                    continue;
                }
            }

            String getMethodText = codeGenerator(MethodType.GET,field);
            PsiMethod getMethod = elementFactory.createMethodFromText(getMethodText, psiClass);
            if (!methodSet.contains(getMethod.getName())) {
                psiClass.add(getMethod);
            }

            String setMethodText = codeGenerator(MethodType.SET,field);
//            elementFactory = JavaPsiFacade.getElementFactory(psiClass.getProject());
            PsiMethod setMethod = elementFactory.createMethodFromText(setMethodText, psiClass);
            if (!methodSet.contains(setMethod.getName())) {
                psiClass.add(setMethod);
            }
        }
    }

    /**
     * 代码生成器
     * @param methodType
     * @param field
     * @return
     */
    private String codeGenerator(MethodType methodType,PsiField field){
        String javadoc = docGenerator(methodType, field);

        String javaCode = "";
        String filedType = field.getType().getPresentableText();
        String filedName = field.getName();
        String filedNameWithUpperCase = filedNameToUpperCase.apply(filedName);

        if(methodType == MethodType.GET)
            javaCode = String.format(filedType.equalsIgnoreCase("boolean") ? GETTER_CODE_TEMPLATE_BOOLEAN : GETTER_CODE_TEMPLATE_DEFAULT,filedType,filedNameWithUpperCase,filedName);

        if(methodType == MethodType.SET)
            javaCode = String.format(SETTER_CODE_TEMPLATE,filedNameWithUpperCase,filedType,filedName,filedName,filedName);

        //如果返回类型为boolean 修改获取为是否 如果文档注释本身有是否就去重
        if(filedType.equalsIgnoreCase("boolean"))
            javadoc = javadoc.replaceFirst("获取","是否").replaceFirst("是否是否","是否");

        return javadoc + javaCode;
    }

    /**
     * 文档注释生成器
     * @param methodType 方法类型
     * @param field 属性
     * @return javadoc
     */
    private String docGenerator(MethodType methodType,PsiField field){
        String docComment;
        if (field.getDocComment() == null) {
            docComment = field.getText().substring(0, field.getText().lastIndexOf("\n") + 1);
        } else {
            docComment = field.getDocComment().getText();
        }
        String fieldComment = docComment.replaceAll("/","").replaceAll("\\*","").replaceAll("\n","").trim();
        String filedName = field.getName();
        if(methodType == MethodType.GET)
            return String.format(GETTER_DOC_TEMPLATE,fieldComment,fieldComment);
        if(methodType == MethodType.SET)
            return String.format(SETTER_DOC_TEMPLATE,fieldComment,filedName,fieldComment);
        //不存在的默认结果,随便返回点什么好了
        return "/**\n */\n";
    }
}
